package uebung03_w;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.PageAttributes.OriginType;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import org.ejml.simple.SimpleEVD;
import org.ejml.simple.SimpleMatrix;

import tools.Arrow;
import tools.ColorConversions;
import tools.Derivative;
import tools.Gauss;

public class NeuerVersuch {
	public static void main(String[] args) {
		try {
			final BufferedImage imgI = ImageIO.read(IterativeKLT.class.getClassLoader().getResourceAsStream("res/b0.png"));
			final BufferedImage imgJ = ImageIO.read(IterativeKLT.class.getClassLoader().getResourceAsStream("res/b1.png"));
			
			final int w = imgI.getWidth(), h = imgI.getHeight();
			
			final BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
			
			int[] rgbI = imgI.getRGB(0, 0, w, h, new int[w*h], 0, w);
			int[] rgbJ = imgJ.getRGB(0, 0, w, h, new int[w*h], 0, w);
			
			// helligkeitsbild
			int[] G = tools.ColorConversions.getFastLuminance(rgbI, new int[w*h]);
			int[] F = tools.ColorConversions.getFastLuminance(rgbJ, new int[w*h]);
			
			int[] gaussIntensities = Gauss.gaussXY(G, w, h, 2, new int[w*h]);
			System.arraycopy(gaussIntensities, 0, G, 0, G.length);

			gaussIntensities = Gauss.gaussXY(F, w, h, 2, gaussIntensities);
			System.arraycopy(gaussIntensities, 0, F, 0, F.length);
			
			int[] pixels = ColorConversions.buildRGB(G, F, new int[w*h], new int[w*h]);
			bi.setRGB(0, 0, w, h, pixels, 0, w);
			
			//Ableitungen
			final int[] dx0 = Derivative.dx(F, w, h, new int[w*h]);
			final int[] dy0 = Derivative.dy(F, w, h, new int[w*h]);

			final int[] dx1 = Derivative.dx(G, w, h, new int[w*h]);
			final int[] dy1 = Derivative.dy(G, w, h, new int[w*h]);
			
//			final int[] dt = Derivative.dt(F, G, new int[w*h]);

			final int neighbours = 10, step = 10;
			final ArrayList<Point2D> uv = new ArrayList<Point2D>();
			final ArrayList<Point2D> xy = new ArrayList<Point2D>();
			final ArrayList<Point2D> eigenvalues = new ArrayList<Point2D>();
			
			for (int y = neighbours, endy = h-neighbours; y<endy; y+=step) {
				for (int x = neighbours, endx = w-neighbours; x<endx; x+=step) {
					double u = 0, v = 0, hXStep = 1, hYStep = 1;
//					while (Math.abs(hXStep)>0.1 && Math.abs(hYStep)>0.1) {
					int cstep = 0;
					while (cstep++<10) {
						double upperx = 0, lowerx = 0, uppery = 0, lowery = 0;

						for (int j = -neighbours; j<neighbours; j++) {
							for (int i = -neighbours; i<neighbours; i++) {
								try {
									int index = x+i+(y+j)*w;
									
									double wx = 1/Math.abs(IterativeKLT.bilerp(dx1, x, y, w, h)-IterativeKLT.bilerp(dx0, x, y, w, h));
									
									double gxminusfxh = IterativeKLT.bilerp(G, x, y, w, h)-IterativeKLT.bilerp(F, x+u, y+v, w, h);
									double fxhSq = IterativeKLT.bilerp(dx0, x+u, y+v, w, h)*IterativeKLT.bilerp(dx0, x+u, y+v, w, h);
									
									upperx += wx*gxminusfxh;
									lowerx += wx*fxhSq;

									double wy = 1/Math.abs(IterativeKLT.bilerp(dy1, x, y, w, h)-IterativeKLT.bilerp(dy0, x, y, w, h));
									
									double gyminusfyh = IterativeKLT.bilerp(G, x, y, w, h)-IterativeKLT.bilerp(F, x+u, y+v, w, h);
									double fyhSq = IterativeKLT.bilerp(dy0, x+u, y+v, w, h)*IterativeKLT.bilerp(dy0, x+u, y+v, w, h);
									
									uppery += wy*gyminusfyh;
									lowery += wy*fyhSq;
									
								}
								catch (Exception e) {
//									e.printStackTrace();
								}
							}
						}
						
						hXStep = upperx/lowerx;
						hYStep = uppery/lowery;
						
						if (!Double.isNaN(hXStep) || !Double.isNaN(hYStep)) {
							u+=hXStep;
							v+=hYStep;
							
						}
						
					}
					xy.add(new Point2D.Double(x,y));
					uv.add(new Point2D.Double(x+u, y+v));
				}
			}
			final AffineTransform zoom = new AffineTransform();
			final JComponent something = new JComponent() {
				{setPreferredSize(new Dimension(imgI.getWidth(),imgI.getHeight()));}
				Arrow arrow = new Arrow();
				
				@Override
				public void paint(Graphics g) {
					Graphics2D g2 = (Graphics2D)g;
					g2.transform(zoom);
					g2.drawImage(bi, 0, 0, this);
					g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
					g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
					g2.setStroke(new BasicStroke(0.1f));
					for (int i = 0; i<xy.size(); i++) {
//						Point2D p = eigenvalues.get(i);
//						if (p.getX()>averageig1 && p.getY()>averageig2) {
//						if (Math.max(p.getX(), p.getY())>max) {
							System.out.println(xy.get(i).distance(uv.get(i)));
							arrow.setArrow(xy.get(i), uv.get(i), 1, 5);
							g2.setColor(Color.magenta);
							g2.fill(arrow);
							g2.setColor(Color.white);
							g2.draw(arrow);
							
//						}
					}
					// pfeile malen
					super.paint(g);
				}
			};
			something.addMouseWheelListener(new MouseWheelListener() {

				@Override
				public void mouseWheelMoved(MouseWheelEvent e) {

					double scale = Math.pow(1.01, e.getWheelRotation());

					try {
						Point2D p = new Point2D.Double(e.getX(), e.getY());
						zoom.inverseTransform(p, p);
						zoom.concatenate(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
						zoom.concatenate(AffineTransform.getScaleInstance(scale,scale));
						zoom.concatenate(AffineTransform.getTranslateInstance(-p.getX(), -p.getY()));

					} catch (NoninvertibleTransformException nte) {
						nte.printStackTrace();
					}
					something.repaint();
				}
			});
			JFrame frame = new JFrame();
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.add(something,BorderLayout.CENTER);
//			JPanel pan = new JPanel();
//			pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS));
			
			
			
//			frame.add(pan,BorderLayout.EAST);
			
			frame.pack();
			frame.setVisible(true);
			
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}
